package org.future.dataanalysis;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.future.dataanalysis.bean.DataUnit;
import org.future.dataanalysis.bean.Node;
import org.future.dataanalysis.bean.RecursionResult;
import org.future.dataanalysis.bean.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author Dongwei-Chen
 * @Date 2023/12/19 9:31
 * @Description
 */
public class DataModelAnalysis implements Serializable {

    private final Logger log = LoggerFactory.getLogger(DataModelAnalysis.class);

    private final Node node;

    public DataModelAnalysis(Properties properties) {
        init(properties);
        Node node = new Node(Type.OBJECT);

        node.putDataField("devSn", "TMNL_DEV_ID");
        Node additionalInfo = new Node(Type.OBJECT);
        additionalInfo.putDataField("userNo", "USER_NO")
                .putDataField("userIP", "USER_IP")
                .putDataField("sendTime", "SEND_TIME");
        node.putObjMap("additionalInfo", additionalInfo);

        Node dataItemList = new Node(Type.ARRAY);
        dataItemList.setFlat(true);

        Node businessNode = new Node(Type.ARRAY);
        businessNode.putDataField("devSn", "DEV_ID")
                .putDataField("productCode", "PRODUCT_CODE")
                .putDataField("protocolType", "PRODUCT_TYPE")
                .putDataField("cmdType", "CMD_TYPE");

        businessNode.putNodeMap("dataItemList", dataItemList);

        node.putNodeMap("businessObjectList", businessNode);
        this.node = node;
    }

    protected final Map<String, AnalysisServer> dataItemAnalysisMap = new HashMap<>();

    protected final Map<String, List<AnalysisServer>> analysisServerMap = new HashMap<>();


    protected static final String BUSINESS_OBJECT_LIST = "businessObjectList";
    protected static final String DATA_ITEM_LIST = "dataItemList";
    protected static final String DATA_ITEM_ID = "dataItemId";
    protected static final String RESPONSE_FLAG = "responseFlag";
    protected static final String DATA_TIME = "dataTime";
    protected static final String DATA_RETURN_TIME = "dataReturnTime";

    public Map<String, AnalysisServer> getDataItemAnalysisMap() {
        return dataItemAnalysisMap;
    }

    public List<DataUnit> analysis(String data, Object partition, Object offset) {
        if (!data.contains(BUSINESS_OBJECT_LIST) || !data.contains(DATA_ITEM_ID) || !data.contains(DATA_ITEM_LIST)) {
            log.warn("数据[{}]数据异常。partition:{},offset:{}", data, partition, offset);
            return null;
        }
        JSONObject jsonObject = JSON.parseObject(data);
        Boolean result = jsonObject.getBoolean("result");
        if (result == null || !result) {
            return null;
        }
        JSONArray businessArray = jsonObject.getJSONArray(BUSINESS_OBJECT_LIST);
        if (businessArray == null || businessArray.isEmpty()) {
            return null;
        }
        RecursionResult fullData = getData(jsonObject, null, node, new RecursionResult(), null);
        if (fullData == null) {
            return null;
        }
        return fullData.getAllDataUnit();
    }

    protected void init(Properties properties) {
        Map<String, Map<String, String>> items = new HashMap<>();
        Set<String> strings = properties.stringPropertyNames();
        for (String key : strings) {
            String property = properties.getProperty(key);
            String[] split = key.split("\\.");
            String dataItemId = split[0];
            Map<String, String> stringStringMap = items.getOrDefault(dataItemId, new HashMap<>(16));
            stringStringMap.put(split[1], property);
            items.put(dataItemId, stringStringMap);
        }
        Set<Map.Entry<String, Map<String, String>>> entries = items.entrySet();
        for (Map.Entry<String, Map<String, String>> entry : entries) {
            String uniqueId = entry.getKey();
            Map<String, String> value = entry.getValue();
            String analysisType = value.get("analysisType");
            switch (analysisType) {
                case "0":
                    ObjectAnalysis objectAnalysis = new ObjectAnalysis(uniqueId, value, null);
                    dataItemAnalysisMap.put(uniqueId, objectAnalysis);
                    break;
                case "1":
                    String collType = value.get("collType");
                    if (StringUtils.isBlank(collType)) {
                        break;
                    }
                    value.put("topic", uniqueId);
                    objectAnalysis = new ObjectAnalysis(uniqueId, value, null);
                    String[] collTypes = collType.split("\\,");
                    for (String type : collTypes) {
                        List<AnalysisServer> analysisServers = analysisServerMap.getOrDefault(type, new ArrayList<>());
                        analysisServers.add(objectAnalysis);
                        analysisServerMap.put(type, analysisServers);
                    }
                    break;
                default:
            }
        }
    }

    protected RecursionResult getData(JSON dataObj, String parent, Node node, RecursionResult data, Type type) {
        if (dataObj == null) {
            return data;
        }
        Map<String, String> dataMap = node.getDataMap();
        if (type == null) {
            type = node.getType();
        }
        switch (type) {
            case OBJECT:
                JSONObject jsonObject = (JSONObject) dataObj;
                AnalysisServer.fullData(data, dataMap, jsonObject);

                Map<String, Node> objMap = node.getObjMap();
                if (objMap != null && !objMap.isEmpty()) {

                    for (Map.Entry<String, Node> objEntity : objMap.entrySet()) {
                        String fieldName = objEntity.getKey();
                        Node childNode = objEntity.getValue();
                        Object json = jsonObject.get(fieldName);
                        JSON jsonObj;
                        if (childNode.getType() == Type.OBJECT && json instanceof String) {
                            String jsonString = (String) json;
                            jsonObj = JSON.parseObject(jsonString);
                        } else {
                            jsonObj = (JSON) json;
                        }
                        data = getData(jsonObj, fieldName, childNode, data, null);
                    }
                }

                Map<String, Node> nodeMap = node.getNodeMap();
                if (nodeMap == null || nodeMap.isEmpty()) {
                    return data;
                }
                for (Map.Entry<String, Node> entry : nodeMap.entrySet()) {
                    String fieldName = entry.getKey();
                    Node childNode = entry.getValue();
                    JSON jsonObj = (JSON) jsonObject.get(fieldName);
                    data = getData(jsonObj, fieldName, childNode, data, null);
                }
                break;
            case ARRAY:
                JSONArray jsonArray = (JSONArray) dataObj;
                RecursionResult singleResult = null;
                if (BUSINESS_OBJECT_LIST.equals(parent)) {
                    //此时 初始化
                    singleResult = new RecursionResult();
                    singleResult.setData(data.getData());
                }

                for (Object json : jsonArray) {
                    JSONObject dataItem = (JSONObject) json;
                    if (DATA_ITEM_LIST.equals(parent)) {
                        String dataItemId = dataItem.getString(DATA_ITEM_ID);
                        if (!dataItemAnalysisMap.containsKey(dataItemId)) {
                            log.warn("未找到数据项{},数据:{}的解析对象，不解析", dataItemId, data);
                            continue;
                        }
                        String responseFlag = dataItem.getString(RESPONSE_FLAG);
                        if (responseFlag == null || (Integer.parseInt(responseFlag) != 0 && Integer.parseInt(responseFlag) != 274)) {
                            log.warn("数据：{}，返回状态异常：{}", data, responseFlag);
                            continue;
                        }
                        String dataTime = dataItem.getString(DATA_TIME);
                        if (dataTime == null) {
                            dataTime = dataItem.getString(DATA_RETURN_TIME);
                        }
                        LocalDate currentDate = LocalDate.parse(dataTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                        LocalDate nowDate = LocalDate.now();
                        LocalDate targetDate = nowDate.minusDays(30);
                        //此处判断 如果在30天以内 正常入库 30天以外丢弃
                        if (!currentDate.isAfter(targetDate) && !currentDate.isEqual(targetDate)) {
                            log.warn("数据项：{}，数据：{}超过30天，不入库", dataItemId, data);
                            continue;
                        }
                        //此处开始 解析dataItemList数据
                        AnalysisServer analysisServer = dataItemAnalysisMap.get(dataItemId);
                        DataUnit dataUnit = new DataUnit();
                        DataUnit unit = data.getData();
                        BeanUtil.copyProperties(unit, dataUnit);
                        dataUnit.setDataArea(new HashMap<>(unit.getDataArea()));
                        RecursionResult analysis = analysisServer.analysis(dataItem, dataUnit);
                        data.getAllDataUnit().addAll(analysis.getAllDataUnit());
                        continue;
                    }
                    DataUnit dataUnit;
                    if (singleResult != null) {
                        dataUnit = singleResult.getData();
                    } else {
                        dataUnit = data.getData();
                    }
                    DataUnit unit = new DataUnit();
                    BeanUtil.copyProperties(dataUnit, unit);
                    data.setData(unit);
                    data = getData(dataItem, parent, node, data, Type.OBJECT);
                    assert data != null;
                    if (node.isFlat()) {
                        data.pushFinalArea(data.getData());
                    }
                }
            default:
        }
        return data;
    }
}
